/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_KNIFE_C_RESOURCE_INTERCEPTOR_OH_H
#define IMAGE_KNIFE_C_RESOURCE_INTERCEPTOR_OH_H

#include "interceptor.h"
#include "imageknife_request.h"
#include <cstdint>
#include <filemanagement/file_uri/error_code.h>
#include <sys/stat.h>
#include <filemanagement/file_uri/oh_file_uri.h>
#include "hitrace/trace.h"

namespace ImageKnifePro {
class ResourceInterceptorDefault : public LoadInterceptor {
public:
    ResourceInterceptorDefault()
    {
        isLoadFromRemote = false;
        name = "Default ResourceInterceptor";
    }

    bool Resolve(std::shared_ptr<ImageKnifeTask> task) override
    {
        Resource resource;
        if (task->GetImageSource()->GetResource(resource)) {
            return LoadImageFromResource(resource, task);
        }

        std::string filePath = task->GetImageSource()->ToString();
        // 本地沙箱路径资源
        int ret = filePath.find("/data/storage");
        if (ret == 0) {
            return ReadFromLocal(task, filePath);
        }

        // 图库图片资源，file:开头的路径
        ret = filePath.find("file:");
        if (ret == 0) {
            char *pathResult = nullptr;
            OH_FileUri_GetPathFromUri(filePath.c_str(), filePath.size(), &pathResult);
            std::string filePath = std::string(pathResult);
            if (pathResult != nullptr) {
                free(pathResult); // 官方接口说明这里必须使用free来释放内存
                pathResult = nullptr;
            }
            return ReadFromLocal(task, filePath);
        }

        // base64图片
        ret = filePath.find("data:image/");
        if (ret == 0) {
            return DecodeBase64(task, filePath);
        }
        return false;
    }

private:
    bool DecodeBase64(std::shared_ptr<ImageKnifeTask> task, std::string &base64Str);

    bool ReadFromLocal(std::shared_ptr<ImageKnifeTask> task, std::string &filePath)
    {
        struct stat fileStat;
        int ret = stat(filePath.c_str(), &fileStat);
        if (ret == -1 || fileStat.st_size <= 0) {
            return false;
        }

        FILE *fd = fopen(filePath.c_str(), "r");
        if (fd == nullptr) {
            return false;
        }

        uint8_t *buffer = static_cast<uint8_t *>(malloc((fileStat.st_size + 1) * sizeof(uint8_t)));
        int64_t length = fread(buffer, sizeof(uint8_t), fileStat.st_size, fd);
        if (length <= 0) {
            free(buffer);
            (void)fclose(fd);
            fd = nullptr;
            return false;
        }
        buffer[length] = 0;
        task->product.imageBuffer = std::shared_ptr<uint8_t[]>(buffer, [](void *ptr) {
            if (ptr != nullptr) {
                free((uint8_t*)ptr);
                ptr = nullptr;
            }
        });
        task->product.imageLength = length;
        (void)fclose(fd);
        fd = nullptr;
        return true;
    }

    bool LoadImageFromResource(Resource resource, std::shared_ptr<ImageKnifeTask> task)
    {
        auto option =  task->GetImageKnifeRequest()->GetImageKnifeOption();
        if (option->context.resourceManager == nullptr) {
            task->EchoError("Get context.resourceManager failed");
            return false;
        }

        uint64_t length = 0;
        ResourceManager_ErrorCode errCode;
        uint8_t *buffer = nullptr;
        // 先尝试获取本地文件
        errCode = OH_ResourceManager_GetMedia(option->context.resourceManager,
                                              resource.id,
                                              &buffer,
                                              &length);
        // 尝试获取跨包文件
        if (errCode != ResourceManager_ErrorCode::SUCCESS && resource.id == -1) {
            errCode = GetBufferFromOtherModule(resource, task, &buffer, length);
        }

        // 尝试获取rawfile文件
        if (errCode != ResourceManager_ErrorCode::SUCCESS) {
            errCode = GetBufferFromRawfile(resource, task, &buffer, length);
        }

        if (errCode != ResourceManager_ErrorCode::SUCCESS) {
            // 获取图片资源失败
            task->EchoError("Get image from resource failed, error code: " + std::to_string(errCode));
            return false;
        }

        if (length > UINT32_MAX) {
            // 默认拦截器支持的length类型为uint32_t
            free(buffer);
            task->EchoError("Resource Image size out of range!");
            return false;
        }

        task->product.imageBuffer = std::shared_ptr<uint8_t[]>(buffer, [](void *ptr) {
            if (ptr != nullptr) {
                free((uint8_t*)ptr);
                ptr = nullptr;
            }
        });
        task->product.imageLength = length;
        return true;
    }

    ResourceManager_ErrorCode GetBufferFromOtherModule(Resource resource,
                                                       std::shared_ptr<ImageKnifeTask> task,
                                                       uint8_t **buffer,
                                                       uint64_t &length)
    {
        auto option =  task->GetImageKnifeRequest()->GetImageKnifeOption();
        // [modeule].media.xxx, 目前目录结构要求的格式，所以这里只按照这个格式来解析文件名，后续变化再做处理
        const char *param = resource.param.c_str();
        const size_t maxFileNameLength = 128;
        char fileName[maxFileNameLength] = {0};
        size_t fileLength = -1;
        size_t index = 0;
        for (int i = resource.param.size() - 1; i >= 0; i--) {
            if (param[i] == '.') {
                index = i + 1;
                break;
            }
        }
        for (int i = index; i < resource.param.size(); i++) {
            fileName[++fileLength] = param[i];
        }
        fileName[++fileLength] = 0;
        return OH_ResourceManager_GetMediaByName(option->context.resourceManager, fileName, buffer, &length);
    }

    ResourceManager_ErrorCode GetBufferFromRawfile(Resource resource,
                                                   std::shared_ptr<ImageKnifeTask> task,
                                                   uint8_t **buffer,
                                                   uint64_t &length)
    {
        auto option =  task->GetImageKnifeRequest()->GetImageKnifeOption();
        RawFile64 *rFile = OH_ResourceManager_OpenRawFile64(option->context.resourceManager, resource.param.c_str());
        length = 0;
        if (rFile != nullptr) {
            uint64_t len = OH_ResourceManager_GetRawFileRemainingLength64(rFile);
            if (*buffer == nullptr) {
                *buffer = static_cast<uint8_t *>(malloc((len + 1) * sizeof(uint8_t)));
            }
            length = OH_ResourceManager_ReadRawFile64(rFile, *buffer, len);
            (*buffer)[length] = 0;
            OH_ResourceManager_CloseRawFile64(rFile);
            rFile = nullptr;
            if (length > 0) {
                return ResourceManager_ErrorCode::SUCCESS;
            }
        }
        return ResourceManager_ErrorCode::ERROR_CODE_RES_ID_NOT_FOUND;
    }
};

};

#endif // IMAGE_KNIFE_C_RESOURCE_INTERCEPTOR_OH_H
